/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/*************************************************************************
* File: network.hpp
*************************************************************************
*\author: starlove
*************************************************************************/
#ifndef __CC_PYTHON_NETWORK_H__
#define __CC_PYTHON_NETWORK_H__

#include <cocos2d.h>
#include "data.hpp"
#include "platform.hpp"
#include "singleton.hpp"
#include "socket.hpp"
#include "codec/python.hpp"
#include <objects/python_object_nodeobjects.h>

NS_CCPY_BEGIN

	class CNetworkLayer : public cocos2d::CCLayer, public CTCPSocket
	{
	public:
		virtual void OnPacketIn(CBufferReader *) = 0;
		bool init()
		{
			CCEXT_RETURN_IF(!CCLayer::init(), false);
			*m_host = 0;
			m_port = 0;
			scheduleUpdateWithPriority(100);
			return true;
		}

		void update(float dt)
		{
			lookup();
		}

		void on_recv(void *p, unsigned int size)
		{
			//cocos2d::CCLog(">>> on_recv");
			CBufferReader reader((const char *)p, size);
			OnPacketIn(&reader);
			//cocos2d::CCLog(">>> on_recv end");
		}
	
		void SetRemote(const char *host, unsigned int port)
		{
			strcpy(m_host, host);
			m_port = port;
			close();
			cocos2d::CCLog("set remote target: %s:%d!", host, port);
		}

		bool Send(IAutoExpandBuffer *packet)
		{
			if(!alive())
			{
				if(*m_host == 0 || m_port == 0)
					return false;
				if(!connect(m_host, m_port))
				{
				cocos2d::CCLog("reconnect request create fail!");
					return false;
				}
				cocos2d::CCLog("reconnect request create suscess!");
			}
			return send(packet);
		}

	private:
		char m_host[255];
		unsigned int m_port;
	};
	
	class CPyNetwork;
	class CNetwork : public CNetworkLayer, public IWrapper
	{
	public:
		~CNetwork()
		{
			Py_SAFE_DECREF(m_fn);
		}

		virtual bool init() { m_fn = NULL; return CNetworkLayer::init(); };
		virtual void OnPacketIn(CBufferReader *p)
		{
			if(m_fn)
			{
				PyObject *pyPacket = PyDict_New();
				m_codec.GetDecoder()->Decode(p, (void *)pyPacket);
				PyObject_CallMethod(m_fn, "on_packet_in", "O", pyPacket);
				Py_SAFE_DECREF(pyPacket);
				if(PyErr_Occurred())
					PyErr_Print();
			}
		}

		void SendPacketOut(PyObject *pyPacket)
		{
			static CAutoExpandBuffer<__NWBUFFER_DEFSIZE__> tmpBuffer;
			tmpBuffer.Reset();
			if(m_codec.GetEncoder()->Encode(&tmpBuffer, (void *)pyPacket))
			{
				Send(&tmpBuffer);
			}
		}

		void setPyObject(PyObject *fn)
		{
			m_fn = fn;
			Py_XINCREF(m_fn);
		}

		PyTypeObject *GetPythonType()
		{
			return CPyClassBase::GetTypeObject<CPyNetwork>();
		}

		PyObject *GetPythonObject()
		{
			return m_fn;
		}

		CREATE_FUNC(CNetwork);

	private:
		CPythonCodec m_codec;
		PyObject *m_fn;
	};

	class CPyNetwork : public CPyCCObject
	{
	PYCLASS_DEF_INTERFACE(CPyNetwork)
	public:
		CPyNetwork()
		{
			CNetwork *wrapper = CNetwork::create();
			if(!wrapper)
				return;
			wrapper->setPyObject((PyObject *)this);
			m_ccobj = wrapper;
		}

		PYCLASS_METHOD_DEF(CPyNetwork, SetRemote)
		{
			char *host = NULL;
			unsigned int port = 0;
			if( !PyArg_ParseTuple(pyArgs, "sI", &host, &port) )
				Py_RETURN_NONE;
			CNetwork *p = dynamic_cast<CNetwork *>(pySelf->getCCObject());
			if(p)
				p->SetRemote(host, port);
			Py_RETURN_NONE;
		}

		PYCLASS_METHOD_DEF(CPyNetwork, on_packet_in)
		{
			Py_RETURN_NONE;
		}

		PYCLASS_METHOD_DEF(CPyNetwork, send)
		{
			PyObject *packet = NULL;
			if( !PyArg_ParseTuple(pyArgs, "O", &packet) )
				Py_RETURN_NONE;
			CNetwork *p = dynamic_cast<CNetwork *>(pySelf->getCCObject());
			if(p)
				p->SendPacketOut(packet);
			Py_RETURN_NONE;
		}

		PYCLASS_METHOD_DEF(CPyNetwork, alive)
		{
			CNetwork *p = dynamic_cast<CNetwork *>(pySelf->getCCObject());
			if(p)
				Py_RETURN_BOOL(p->alive());
			Py_RETURN_FALSE;
		}

		PYCLASS_METHOD_DEF(CPyNetwork, close)
		{
			CNetwork *p = dynamic_cast<CNetwork *>(pySelf->getCCObject());
			if(p)
				p->close();
			Py_RETURN_NONE;
		}
	};

	PYCLASS_INIT_AND_BASE(CPyNetwork, CPyLayer, "CCNetwork", "Python CCNetwork Object");
	PYCLASS_BEGIN_METHOD_DEF(CPyNetwork)
		PYCLASS_ADD_METHOD(CPyNetwork, SetRemote)
		PYCLASS_ADD_METHOD(CPyNetwork, on_packet_in)
		PYCLASS_ADD_METHOD(CPyNetwork, send)
		PYCLASS_ADD_METHOD(CPyNetwork, alive)
		PYCLASS_ADD_METHOD(CPyNetwork, close)
	PYCLASS_END_METHOD_DEF(CPyNetwork)
	PYCLASS_BEGIN_GETSET_DEF(CPyNetwork)
	PYCLASS_END_GETSET_DEF(CPyNetwork)
	PYCLASS_IMP_INTERFACE(CPyNetwork)

NS_CCPY_END

#endif // !__CC_PYTHON_NETWORK_H__